package com.gosophia.commons;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.dozer.DozerBeanMapper;
import org.dozer.Mapper;

import com.gosophia.commons.DozerInfoSet;
import com.gosophia.commons.DozerProperty;

//FIXME: Entity 不确定DTO的属性信息
/**
 * 
 * 功能： dozer转化工具类， 借助@DozerInfoSet 和@DozerProperty标注信息解析对象属性的转换关系，
 * 如果没有添加标注，使用默认dozer.map进行转换
 * 
 * @author renxuehui
 * @since 2010-9-30 Copyright (c) 2010 Gosophia corporation All Rights Reserved
 */
public class DozerChangeUtil {
    private static Mapper dozer;
    static {
        dozer = new DozerBeanMapper();
    }

    /**
     * 对象之间转化
     * 
     * @param <TBefore>
     * @param <TAfter>
     * @param beforeObj
     *            需要转的对象
     * @param afterType
     *            目标对象的类型
     * @return 目标对象
     * @throws NoSuchFieldException
     * @throws SecurityException
     * @throws IllegalAccessException
     * @throws IllegalArgumentException
     */
    @SuppressWarnings( { "null", "unchecked" })
    public static <TBefore, TAfter> TAfter changeBeforeToAfter(TBefore beforeObj, Class afterType) {
        if (beforeObj == null) {
            throw new IllegalArgumentException("No destination bean specified");
        }

        //FIXME:出现异常的情况
        TAfter after = null;
        // 使用dozer进行默认转换
        try{
        	after = (TAfter) dozer.map(beforeObj, afterType);
        }catch (Exception e) {//
			e.printStackTrace();
		}
        
        if(after == null){
            return null;
        }

        Boolean bAccessible = null; // 记录对象属性默认访问标记
        Field fAfter = null; // 记录转换后对象属性
        try {
            // 对转换后对象属性进行迭代
            for (Field f : after.getClass().getDeclaredFields()) {
                bAccessible = f.isAccessible(); // 记住对象属性默认访问标记
                fAfter = f;
                f.setAccessible(true); // 是对象属性可访问

                // 如果默认dozer已经转换，此次不再进行第二次转换，直接对下一个属性进行扫描。
                // （根据需要可去掉这部分代码）
//                if (f.get(after) != null) {
//                    f.setAccessible(bAccessible);
//                    continue;
//                }

                // 取得对象属性上的转换标注对象
                DozerProperty d = null;
                DozerInfoSet dSet = f.getAnnotation(DozerInfoSet.class);

                // 查看方法上是否有 转换 标注
                if (dSet != null) { // 指定了转换类的标注
                    for (DozerProperty dp : dSet.dozerProperty()) {
                        if (dp.beforeClass().equals(beforeObj.getClass())) {
                            d = dp;
                            break;
                        }
                    }
                } else { // 未指定转换类的标注
                    d = f.getAnnotation(DozerProperty.class);
                }

                if (d != null) { // 如果有转换标注，对标注进行解析
                    if (!d.beforeClass().equals(DozerProperty.class) // 如果转换前属性类型不是默认类型（即用户指定了类型）
                            && !d.beforeClass().equals(beforeObj.getClass())) {// 并且指定的类型和传入类型不一致，不予转换
                        break;
                    }

                    String[] befFieldNames; // 按"."拆分转换前对象属性信息
                    String[] myFieldNames; // 按"."拆分转换后对象属性信息
                    Object fieldBefore = null; // 记录转换前属性引用对象
                    Object fieldAfter = null; // 记录转换后属性引用对象
                    Class befType = null;

                    if (!d.beforeField().isEmpty()) {// 如果转换前属性不为""
                        befFieldNames = d.beforeField().split("\\."); // 按"."拆分转换前对象属性信息

                        // 通过迭代，取得转换前对象的属性引用对象
                        fieldBefore = beforeObj;
                        for (String fieldName : befFieldNames) {
                            Field objField = fieldBefore.getClass().getDeclaredField(fieldName);
                            Boolean bFieldAccessible = objField.isAccessible();

                            objField.setAccessible(true);
                            fieldBefore = objField.get(fieldBefore);
                            objField.setAccessible(bFieldAccessible);

                            if (fieldName.equals(befFieldNames[befFieldNames.length - 1])) {
                                if(Collection.class.isAssignableFrom(objField.getType())){
                                    ParameterizedType ptAft = (ParameterizedType) objField.getGenericType();
                                    befType = (Class) ptAft.getActualTypeArguments()[0];
                                }else{
                                    befType = objField.getType();
                                }
                            }

                            if (fieldBefore == null) {// 如果找到的属性是null，不必再查找它的子属性
                                break; // break the : for (String fieldName :
                                       // befFieldNames) {
                            }
                        }
                    } else {// 没有指定转换前属性，不转换，扫描下一个属性
                        continue;
                    }

                    if (!d.myField().isEmpty()) {// 如果转换后属性不为""
                        myFieldNames = d.myField().split("\\."); // 按"."拆分转换后对象属性信息

                        // 通过迭代，取得转换后对象的属性引用对象
                        fieldAfter = after;
                        for (String fieldName : myFieldNames) {
                            Object objParent = fieldAfter;
                            Field objField = fieldAfter.getClass().getDeclaredField(fieldName);

                            Boolean bFieldAccessible = objField.isAccessible();
                            objField.setAccessible(true);

                            if (fieldName.equals(myFieldNames[myFieldNames.length - 1])) {// 找到最终要转换成的属性信息

                                // 如果转换前对象的属性为null
                                if (fieldBefore == null || befType == null) {
                                    fieldAfter = fieldBefore;
                                }
                                // 如果转换后对象是Collection类型（如：ArrayList<T>,HashSet<T>等），调用转换集合方法
                                else if (Collection.class.isAssignableFrom(objField.getType())) {
                                    ParameterizedType ptAft = (ParameterizedType) objField.getGenericType();
                                    Class type = (Class) ptAft.getActualTypeArguments()[0];

                                    if (befType.equals(type)) {//如果类型相等不必转换，否则转换
                                        fieldAfter = fieldBefore;
                                    } else {
                                        fieldAfter = changeBeforeToAfter((Collection<TBefore>) fieldBefore, type,
                                                objField.getType());
                                    }
                                }
                                // 除以上情况，如果转换前和转换后对象的属性类型一致，直接赋值
                                else if (objField.getType().equals(befType)) {
                                    fieldAfter = fieldBefore;
                                }
                                // 除以上情况，如果转换前和转换后对象的属性类型不一致，递归调用
                                else {
//                                    fieldAfter = changeBeforeToAfter(fieldBefore, fieldAfter.getClass());
                                    fieldAfter = changeBeforeToAfter(fieldBefore, objField.getType());
                                }
                            } else {
                                try {
                                    fieldAfter = objField.getType().getConstructor().newInstance();
                                } catch (NoSuchMethodException e) {
                                    e.printStackTrace();
                                    throw new IllegalArgumentException("指定的beforeField或beforeField所在类不能被实例化，\r\n"
                                            + "请检查类是否有缺省的构造函数!");
                                }
                            }
                            objField.set(objParent, fieldAfter);
                            objField.setAccessible(bFieldAccessible);
                        }
                    } else {
                        f.set(after, fieldBefore);
                    }
                }
                f.setAccessible(bAccessible);
            }
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
            fAfter.setAccessible(bAccessible);
            throw new IllegalArgumentException("找不到指定的beforeField!");
        } catch (SecurityException e) {
            e.printStackTrace();
            fAfter.setAccessible(bAccessible);
            throw new IllegalArgumentException("找不到指定的beforeField!");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            fAfter.setAccessible(bAccessible);
            throw new IllegalArgumentException("不能访问指定的beforeField!");
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
            fAfter.setAccessible(bAccessible);
            throw new IllegalArgumentException("找不到指定的beforeField!");
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            fAfter.setAccessible(bAccessible);
            throw new IllegalArgumentException("找不到指定的beforeField!");
        }

        return after;
    }

    /**
     * 根据类型转换出指定的list对象
     * 
     * @param <TBefore>
     * @param <TAfter>
     * @param beforeObjs
     *            需要转的对象
     * @param afterType
     *            目标对象的类型
     * @return 目标对象
     */
    @SuppressWarnings("unchecked")
    public static <TBefore, TAfter> List<TAfter> changeBeforeToAfter(List<TBefore> beforeObjs, Class afterType) {
        if (beforeObjs == null) {
            throw new IllegalArgumentException("No destination bean specified");
        }

        List<TAfter> rslt = new ArrayList<TAfter>();

        for (TBefore beforeObj : beforeObjs) {
            TAfter after = (TAfter) changeBeforeToAfter(beforeObj, afterType);
            rslt.add(after);
        }
        return rslt;
    }

    /**
     * 根据类型转换出指定的list对象
     * 
     * @param <TBefore>
     * @param <TAfter>
     * @param beforeObjs
     *            需要转的对象
     * @param afterType
     *            目标对象的类型
     * @return 目标对象
     */
    @SuppressWarnings("unchecked")
    private static <TBefore, TAfter> Collection<TAfter> changeBeforeToAfter(Collection<TBefore> beforeObjs,
            Class afterType, Class rsltType) {
        if (beforeObjs == null) {
            throw new IllegalArgumentException("No destination bean specified");
        }
        Collection<TAfter> rslt;
        if (rsltType.equals(Set.class)) {
            rslt = new HashSet<TAfter>();
        } else {
            rslt = new ArrayList<TAfter>();
        }

        for (TBefore beforeObj : beforeObjs) {
            TAfter after = (TAfter) changeBeforeToAfter(beforeObj, afterType);
            rslt.add(after);
        }
        return rslt;
    }
}
